package com.example.common.tools

import java.io.BufferedInputStream
import java.io.FileInputStream
import java.io.IOException

class WaveFileReader {
    private var filename: String? = null
    private var data: Array<ByteArray>? = null
    private var len = 0
    private var chunkdescriptor: String? = null
    private val lenchunkdescriptor = 4
    private var chunksize: Long = 0
    private val lenchunksize = 4
    private var waveflag: String? = null
    private val lenwaveflag = 4
    private var fmtubchunk: String? = null
    private val lenfmtubchunk = 4
    private var subchunk1size: Long = 0
    private val lensubchunk1size = 4
    private var audioformat = 0
    private val lenaudioformat = 2
    private var numchannels = 0
    private val lennumchannels = 2
    private var samplerate: Long = 0
    private val lensamplerate = 2
    private var byterate: Long = 0
    private val lenbyterate = 4
    private var blockalign = 0
    private val lenblockling = 2
    private var bitspersample = 0
    private val lenbitspersample = 2
    private var datasubchunk: String? = null
    private val lendatasubchunk = 4
    private var subchunk2size: Long = 0
    private val lensubchunk2size = 4
    private var fis: FileInputStream? = null
    private var bis: BufferedInputStream? = null
    private var issuccess = false

    constructor(filename: String){
        this.filename = filename
        initReader(filename)
    }

    // 判断是否创建wav读取器成功
    fun isSuccess(): Boolean {
        return issuccess
    }

    // 获取每个采样的编码长度，8bit或者16bit
    fun getBitPerSample(): Int {
        return this.bitspersample
    }

    // 获取采样率
    fun getSampleRate(): Long {
        return this.samplerate
    }

    // 获取声道个数，1代表单声道 2代表立体声
    fun getNumChannels(): Int {
        return this.numchannels
    }

    // 获取数据长度，也就是一共采样多少个
    fun getDataLen(): Int {
        return this.len
    }

    // 获取数据
    // 数据是一个二维数组，[n][m]代表第n个声道的第m个采样值
    fun getData(): Array<ByteArray> {
        return this.data!!
    }

    private fun initReader(filename: String) {
        this.filename = filename
        try {
            fis = FileInputStream(this.filename)
            bis = BufferedInputStream(fis)
            chunkdescriptor = readString(lenchunkdescriptor)
            require(chunkdescriptor!!.endsWith("RIFF")) { "RIFF miss, $filename is not a wave file." }
            chunksize = readLong()
            waveflag = readString(lenwaveflag)
            require(waveflag!!.endsWith("WAVE")) { "WAVE miss, $filename is not a wave file." }
            fmtubchunk = readString(lenfmtubchunk)
            require(fmtubchunk!!.endsWith("fmt ")) { "fmt miss, $filename is not a wave file." }
            subchunk1size = readLong()
            audioformat = readInt()
            numchannels = readInt()
            samplerate = readLong()
            byterate = readLong()
            blockalign = readInt()
            bitspersample = readInt()
            datasubchunk = readString(lendatasubchunk)
            require(datasubchunk!!.endsWith("data")) { "data miss, $filename is not a wave file." }
            subchunk2size = readLong()
            len = (subchunk2size / (bitspersample / 8) / numchannels).toInt()
            data = Array(numchannels) { ByteArray(len) }
            for (i in 0 until len) {
                for (n in 0 until numchannels) {
                    if (bitspersample == 8) {
                        data!![n][i] = bis!!.read().toByte()
                    } else if (bitspersample == 16) {
                        data!![n][i] = this.readInt().toByte()
                    }
                }
            }
            issuccess = true
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            try {
                if (bis != null) bis!!.close()
                if (fis != null) fis!!.close()
            } catch (e1: Exception) {
                e1.printStackTrace()
            }
        }
    }

    private fun readString(len: Int): String {
        val buf = ByteArray(len)
        try {
            if (bis!!!!.read(buf) != len) throw IOException("no more data!!!")
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return String(buf)
    }

    private fun readInt(): Int {
        val buf = ByteArray(2)
        var res = 0
        try {
            if (bis!!.read(buf) != 2) throw IOException("no more data!!!")
            res = (buf[0].toInt() and 0x000000FF) or (buf[1].toInt() shl 8)
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return res
    }

    private fun readLong(): Long {
        var res = 0
        try {
            var l = LongArray(4)
            for (i in l.indices) {
                l[i] = bis!!.read().toLong()
                if (l[i].toInt() == -1) {
                    throw IOException("no more data!!!")
                }
            }

            res =
                l[0].toInt() or (l[1].toInt() shl 8) or (l[2].toInt() shl 16) or (l[3].toInt() shl 24)
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return res.toLong()
    }

    private fun readBytes():ByteArray{
        val buf = ByteArray(len)
        try {
            if (bis!!.read(buf) != len) throw IOException("no more data!!!")
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return buf
    }

}

